/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Vector4_h__
#define __dylab_Vector4_h__

//===========================================================================================
#include <dylab/utils/geometry/Vector3.h>

//===========================================================================================
namespace dylab {

    /** 
     */
    class DYLAB_EXPORT Vector4 
    {
    public:
        /// zero and unit vector constants
        static const Vector4 ZERO;
        static const Vector4 ONE;
        ///
        static Vector4 random(const Vector4 & _exclude = ZERO);
    protected:       
        /// the point's coordinates        
        real_t mX, mY, mZ, mW;
    public:

        //-----------------------------------------------------------------------------
        // constructors
        //-----------------------------------------------------------------------------

        Vector4() 
                { }

        Vector4(real_t _x, real_t _y, real_t _z, real_t _w) 
            : mX(_x), mY(_y), mZ(_z), mW(_w)
                { }

        Vector4(const Vector4 & _v)
            : mX(_v.mX), mY(_v.mY), mZ(_v.mZ), mW(_v.mW)
                { }

        Vector4(const ArrayReal<4> & _v)
            : mX(_v[0]), mY(_v[1]), mZ(_v[2]), mW(_v[3])
                { } 

        //-----------------------------------------------------------------------------
        // attribut access
        //-----------------------------------------------------------------------------

        /** accesss the point's _x coordinate
         */
        real_t & x() 
                { return mX; }

        /** accesss the point's _x coordinate, without premission to modify it
         */
        const real_t & x() const
                { return mX; }

        /** accesss the point's _y coordinate
         */
        real_t & y() 
                { return mY; }

        /** accesss the point's _y coordinate, without premission to modify it
         */
        const real_t & y() const
                { return mY; }

        /** accesss the point's _z coordinate
         */
        real_t & z() 
                { return mZ; }

        /** accesss the point's _z coordinate, without premission to modify it
         */
        const real_t & z() const
                { return mZ; }

        /** accesss the point's _w coordinate
         */
        real_t & w() 
                { return mW; }

        /** accesss the point's _w coordinate, without premission to modify it
         */
        const real_t & w() const
                { return mW; }

        //-----------------------------------------------------------------------------
        // manipulation
        //-----------------------------------------------------------------------------

        /**
        */
        Vector3 getVector3() const
                { return Vector3(mX, mY, mZ); }

        /**
        */
        Vector3 getVector3Weighted() const
                { return Vector3(mX / mW, mY / mW, mZ / mW); }
        /**
        */
        Vector4 inverse() const
                { return Vector4(-mX, -mY, -mZ, -mW); }

        /**
        */
        Vector4 & add(const Vector4 & _v) 
                { mX += _v.mX, mY += _v.mY, mZ += _v.mZ, mW += _v.mW; return *this; }

        /**
        */
        Vector4 & add(real_t _x) 
                { return add(Vector4(_x, _x, _x, _x)); }
        
        /**        
         */
        Vector4 & sub(const Vector4 & _v) 
                { mX -= _v.mX, mY -= _v.mY, mZ -= _v.mZ, mW -= _v.mW; return *this; }

        /**        
         */
        Vector4 & sub(real_t _x) 
                { return sub(Vector4(_x, _x, _x, _x)); }

        /**
        */
        Vector4 & mul(real_t _x) 
                { mX *= _x, mY *= _x, mZ *= _x, mW *= _x; return *this; }

        /**        
         */
        Vector4 & div(real_t _x) 
                { mX /= _x, mY /= _x, mZ /= _x, mW /= _x; return *this; }

        /**
        */
        real_t dot(const Vector4 & _v) const
                { return mX * _v.mX + mY * _v.mY + mZ * _v.mZ + mW * _v.mW; }

        /**
        */
        real_t sum() const
                { return mX + mY + mZ + mW; }

        /**
        */
        real_t length() const 
                { return Math::squareRoot(dot(*this)); }

        /**
        */
        Vector4 & normalise() 
                { real_t len = length(); if (len > 0) div(len); return *this; }

        /**
        */
        bool almostEqual(const Vector4 & _v, real_t _tolerance = Math::EQUALITY_TOLERANCE) const
                { return Math::almostEqual(mX, _v.mX, _tolerance) && Math::almostEqual(mY, _v.mY, _tolerance) && Math::almostEqual(mZ, _v.mZ, _tolerance) && Math::almostEqual(mW, _v.mW, _tolerance); }

        /**
        */
        bool almostZero(real_t _tolerance = Math::EQUALITY_TOLERANCE) const
                { return almostEqual(ZERO, _tolerance); }

        //-----------------------------------------------------------------------------
        // operators
        //-----------------------------------------------------------------------------

        /**
        */
        Vector4 operator - () const
                { return inverse(); }

        /**
        */
        Vector4 operator + (const Vector4 & _v) const
                { return Vector4(mX + _v.mX, mY + _v.mY, mZ + _v.mZ, mW + _v.mW); }

        /**
        */
        Vector4 operator - (const Vector4 & _v) const
                { return Vector4(mX - _v.mX, mY - _v.mY, mZ - _v.mZ, mW - _v.mW); }

        /**
        */
        Vector4 operator * (real_t _x) const
                { return Vector4(mX * _x, mY * _x, mZ * _x, mW * _x); }

        /**
        */
        Vector4 operator / (real_t _x) const
                { return Vector4(mX / _x, mY / _x, mZ / _x, mW / _x); }

        /**
        */
        Vector4 & operator = (const Vector4 & _v) 
                { mX = _v.mX, mY = _v.mY, mZ = _v.mZ, mW = _v.mW; return *this; }

        /**
        */
        Vector4 & operator += (const Vector4 & _v) 
                { return add(_v); }

        /**
        */
        Vector4 & operator += (real_t _x) 
                { return add(_x); }

        /**
        */
        Vector4 & operator -= (const Vector4 & _v) 
                { return sub(_v); }

        /**
        */
        Vector4 & operator -= (real_t _x) 
                { return sub(_x); }

        /**
        */
        Vector4 & operator *= (real_t _x) 
                { return mul(_x); }

        /**
        */
        Vector4 & operator /= (real_t _x) 
                { return div(_x); }

        /**
        */
        bool operator == (const Vector4 & _v) const
                { return (mX == _v.mX) && (mY == _v.mY) && (mZ == _v.mZ) && (mW == _v.mW); }

        /**
        */
        bool operator != (const Vector4 & _v) const
                { return (mX != _v.mX) || (mY != _v.mY) || (mZ != _v.mZ) || (mW != _v.mW); }

        /**
        */
        bool operator > (const Vector4 & v) const
                { return (mX > v.mX) && (mY > v.mY) && (mZ > v.mZ) && (mW > v.mW); }

        /**
        */
        bool operator >= (const Vector4 & v) const
                { return (mX >= v.mX) && (mY >= v.mY) && (mZ >= v.mZ) && (mW >= v.mW); }

        /**
        */
        bool operator < (const Vector4 & v) const
                { return (mX < v.mX) && (mY < v.mY) && (mZ < v.mZ) && (mW < v.mW); }

        /**
        */
        bool operator <= (const Vector4 & v) const
                { return (mX <= v.mX) && (mY <= v.mY) && (mZ <= v.mZ) && (mW <= v.mW); }

        //-----------------------------------------------------------------------------
        // conversion
        //-----------------------------------------------------------------------------

        Vector3 toVector3() const
                { return Vector3(mX, mY, mZ); }

        operator String() const                        
                { return String::fmt("%1% %2% %3% %4%", mX, mY, mZ, mW); }
    };

    //===========================================================================================    
    inline std::ostream & operator << (std::ostream & _os, const Vector4 & _o)
            { return _os << String(_o); }

    //===========================================================================================
    template <>
        inline Vector4 TypeParser::parse(const String & _string)
            { return Vector4(ArrayReal<4>::parse(_string)); }
    template <>
        inline Vector4 TypeParser::parse(const String & _string, const Vector4 & _defaultValue)
            {
                try { return TypeParser::parse<Vector4>(_string); }
                catch (EInvalidFormat &) { return _defaultValue; }
            }
}

//===========================================================================================
#endif // __dylab_Vector4_h__
